package cas

import (
	"gitee.com/hxchjm/go-admin/pkg/db"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	gormAdapter "github.com/casbin/gorm-adapter/v3"
	redisWatcher "github.com/casbin/redis-watcher/v2"
	_ "github.com/go-sql-driver/mysql"
	"github.com/hxchjm/config"
	"github.com/hxchjm/log"
	"github.com/redis/go-redis/v9"
	"sync"
)

// Initialize the model from a string.
var text = `
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && (keyMatch2(r.obj, p.obj) || keyMatch(r.obj, p.obj)) && (r.act == p.act || p.act == "*")
`

var (
	enforcer *casbin.SyncedEnforcer
	once     sync.Once
)

func Init() error {
	//Apter, err := gormAdapter.NewAdapterByDBUseTableName(dao.db, "sys", "casbin_rule")
	/*	var dc db.DbCfg
		if err := config.Get("DB", &dc); err != nil {
			return err
		}
		dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/",
			dc.User, dc.Password, dc.Addr, dc.Port)*/
	//Apter, err := adapter.NewAdapter("mysql", dsn)
	apter, err := gormAdapter.NewAdapterByDB(db.GetDB())
	if err != nil && err.Error() != "invalid DDL" {
		panic(err)
	}

	m, err := model.NewModelFromString(text)
	if err != nil {
		panic(err)
	}
	enforcer, err = casbin.NewSyncedEnforcer(m, apter)
	if err != nil {
		panic(err)
	}
	if err = enforcer.LoadPolicy(); err != nil {
		return err
	}
	// set redis watcher if redis config is not nil
	rc := db.RdCfg{}
	if err = config.Get("RD", &rc); err != nil {
		return err
	}
	if rc.Addr != "" {
		w, err := redisWatcher.NewWatcher(rc.Addr, redisWatcher.WatcherOptions{
			Options: redis.Options{
				Network:  "tcp",
				Password: rc.Password,
			},
			Channel:    "/casbin",
			IgnoreSelf: false,
		})
		if err != nil {
			panic(err)
		}

		if err = w.SetUpdateCallback(updateCallback); err != nil {
			return err
		}
		if err = enforcer.SetWatcher(w); err != nil {
			return err
		}
	}

	enforcer.EnableLog(true)
	return nil
}

func updateCallback(msg string) {
	log.Info("casbin updateCallback msg: %v", msg)
	err := enforcer.LoadPolicy()
	if err != nil {
		log.Errorf("casbin LoadPolicy err: %v", err)
	}
}

func GetIns() *casbin.SyncedEnforcer {
	if enforcer == nil {
		once.Do(func() {
			Init()
		})
	}
	return enforcer
}
